#include "stdafx.h"
#pragma warning(disable : 4786)

#include "XHtmlTree.h"
#include "CreateCheckboxImageList.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#ifndef __noop
#if _MSC_VER < 1300
#define __noop ((void)0)
#endif
#endif

#undef TRACE
#define TRACE __noop


//=============================================================================
// if you want to see the TRACE output, uncomment this line:
//#include "XTrace.h"
//=============================================================================


//=============================================================================
// REGISTERED XHTMLTREE MESSAGES
//=============================================================================
UINT WM_XHTMLTREE_CHECKBOX_CLICKED = ::RegisterWindowMessage(_T("WM_XHTMLTREE_CHECKBOX_CLICKED"));
UINT WM_XHTMLTREE_ITEM_EXPANDED    = ::RegisterWindowMessage(_T("WM_XHTMLTREE_ITEM_EXPANDED"));

#pragma warning(disable : 4996)    // disable bogus deprecation warning

const UINT HOT_TIMER            = 1;
const UINT LBUTTONDOWN_TIMER    = 2;

int XHTMLTREEDATA::nCount        = 0;

//=============================================================================
BEGIN_MESSAGE_MAP(CXHtmlTree, CTreeCtrl)
//=============================================================================
    //{{AFX_MSG_MAP(CXHtmlTree)
    ON_WM_DESTROY()
    ON_WM_ERASEBKGND()
    ON_WM_MOUSEMOVE()
    ON_WM_SYSCOLORCHANGE()
    ON_WM_TIMER()
    ON_WM_LBUTTONDOWN()
    ON_WM_SIZE()
    ON_WM_RBUTTONDOWN()
    ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
    ON_NOTIFY_REFLECT_EX(NM_CLICK, OnClick)
    ON_NOTIFY_REFLECT_EX(NM_DBLCLK, OnDblclk)
    //}}AFX_MSG_MAP

    ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnSelchanged)
    ON_NOTIFY_REFLECT_EX(TVN_SELCHANGING, OnSelchanging)
END_MESSAGE_MAP()

//=============================================================================
CXHtmlTree::CXHtmlTree()
//=============================================================================
  :    m_bDestroyingTree(FALSE),
    m_bFirstTime(TRUE),
    m_bSmartCheck(FALSE),
    m_bCheckBoxes(FALSE),
    m_bSelectFollowsCheck(TRUE),
    m_bImages(TRUE),
    m_hHotItem(0),
    m_hPreviousItem(0),
    m_hItemButtonDown(0),
    m_hPreviousDropItem(0),
    m_nPadding(0),
    m_nImageHeight(16),
    m_crCustomWindow(-1),
    m_crCustomWindowText(-1)
{
    TRACE(_T("in CXHtmlTree::CXHtmlTree\n"));
    SetColors();
}

//=============================================================================
CXHtmlTree::~CXHtmlTree()
//=============================================================================
{
    if (m_StateImage.GetSafeHandle())
        m_StateImage.DeleteImageList();

    TRACE(_T("XHTMLTREEDATA::nCount=%d\n"), XHTMLTREEDATA::nCount);
}

//=============================================================================
CXHtmlTree& CXHtmlTree::Initialize(BOOL bCheckBoxes /*= FALSE*/, 
                                   BOOL bToolTip /*= FALSE*/)
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::Initialize\n"));
    m_bDestroyingTree = TRUE;

    DeleteAllItems();

    SetImageList(NULL, TVSIL_STATE);

    if (m_StateImage.GetSafeHandle())
        m_StateImage.DeleteImageList();

    m_bCheckBoxes         = bCheckBoxes;
    m_bSmartCheck         = FALSE;
    m_bSelectFollowsCheck = TRUE;
    m_nPadding            = 0;
    m_nImageHeight        = 16;
    m_bFirstTime          = TRUE;
    m_hHotItem            = 0;
    SetColors();

    if (m_bCheckBoxes)
        CreateCheckboxImages();

    m_bDestroyingTree = FALSE;

    return *this;
}

//=============================================================================
void CXHtmlTree::PreSubclassWindow()
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::PreSubclassWindow\n"));
    DWORD dwStyle = GetStyle();

    if (dwStyle & TVS_CHECKBOXES)
        m_bCheckBoxes = TRUE;

    // these styles must not be set
    ModifyStyle(TVS_CHECKBOXES, TVS_NOTOOLTIPS);

    ModifyStyle(0, TVS_DISABLEDRAGDROP);

    if (m_bCheckBoxes)
        CreateCheckboxImages();

    CTreeCtrl::PreSubclassWindow();
}

//=============================================================================
BOOL CXHtmlTree::PreTranslateMessage(MSG* pMsg) 
//=============================================================================
{
    // allow edit control to receive messages, if 
    // label is being edited
    if (GetEditControl() && 
        ((pMsg->message == WM_CHAR) ||
         (pMsg->message == WM_KEYDOWN) ||
         GetKeyState(VK_CONTROL)))
    {
        ::TranslateMessage(pMsg);
        ::DispatchMessage(pMsg);
        return TRUE;
    }

    //=========================================================================
    // WM_CHAR
    //=========================================================================
    if (pMsg->message == WM_CHAR)
    {
        if ((pMsg->wParam == VK_SPACE) && m_bCheckBoxes /*&& !m_bReadOnly*/)
        {
            HTREEITEM hItem = GetSelectedItem();
            
            XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
    
            if (pXTCD && !pXTCD->bSeparator)        //+++1.6
            {
                SetCheck(hItem, !pXTCD->bChecked);
            }

            return TRUE;
        }
    }

    //=========================================================================
    // WM_KEYDOWN
    //=========================================================================
    if (pMsg->message == WM_KEYDOWN)
    {
        TRACE(_T("WM_KEYDOWN: lParam=0x%X\n"), pMsg->lParam);


        //=====================================================================
        // VK_RIGHT or VK_LEFT
        //=====================================================================
        if ((pMsg->wParam == VK_RIGHT) || (pMsg->wParam == VK_LEFT))
        {
            BOOL bRight = pMsg->wParam == VK_RIGHT;

            HTREEITEM hItem = GetSelectedItem();
            
            XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
        
            if (pXTCD && pXTCD->bEnabled && pXTCD->nChildren)
            {
                BOOL bExpanded = pXTCD->bExpanded;
                BOOL bOldExpanded = pXTCD->bExpanded;
                if (!bExpanded && bRight)
                    bExpanded = TRUE;
                if (bExpanded && !bRight)
                    bExpanded = FALSE;
                if (bOldExpanded != bExpanded)
                {
                    Expand(hItem, bExpanded ? TVE_EXPAND : TVE_COLLAPSE);
                    return TRUE;
                }
            }
        }

        //=====================================================================
        // VK_DOWN or VK_UP
        //=====================================================================
        if ((pMsg->wParam == VK_DOWN) || (pMsg->wParam == VK_UP))
        {
            BOOL bDown = pMsg->wParam == VK_DOWN;

            HTREEITEM hItem = GetSelectedItem();
            
            if (hItem)
            {
                HTREEITEM hItemNew = bDown ? GetNextVisibleItem(hItem) : 
                                             GetPrevVisibleItem(hItem);

                XHTMLTREEDATA *pXTCD = NULL;

                while (hItemNew)
                {
                    pXTCD = GetItemDataStruct(hItemNew);
            
                    if (pXTCD && pXTCD->bEnabled)
                        break;

                    // next item is not enabled, just skip it
                    hItemNew = bDown ? GetNextVisibleItem(hItemNew) : 
                                       GetPrevVisibleItem(hItemNew);
                }

                if (hItemNew)
                {
                    SelectItem(hItemNew);
                    return TRUE;
                }
            }
        }

        //=====================================================================
        // VK_MULTIPLY
        //=====================================================================
        if (pMsg->wParam == VK_MULTIPLY)
        {
            HTREEITEM hItem = GetSelectedItem();
            
            if (hItem)
            {
                ExpandBranch(hItem);
                EnsureVisible(hItem);
                SendMessage(WM_HSCROLL, SB_LEFT);
                return TRUE;
            }
        }

        //=====================================================================
        // VK_SUBTRACT  VK_ADD
        //=====================================================================
        UINT nCode = 0;
        switch (pMsg->wParam)
        {
            default:            break;
            case VK_SUBTRACT:    nCode = TVE_COLLAPSE; break;
            case VK_ADD:        nCode = TVE_EXPAND; break;
        }
        if (nCode)
        {
            HTREEITEM hItem = GetSelectedItem();
            if (hItem)
            {
                Expand(hItem, nCode);
                return TRUE;    // skip default processing
            }
        }

    }
    return CTreeCtrl::PreTranslateMessage(pMsg);
}

//=============================================================================
void CXHtmlTree::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
//=============================================================================
{
    NMTVCUSTOMDRAW* pCD = reinterpret_cast<NMTVCUSTOMDRAW*>(pNMHDR);

    CDC* pDC = CDC::FromHandle(pCD->nmcd.hdc);

    HTREEITEM hItem = reinterpret_cast<HTREEITEM> (pCD->nmcd.dwItemSpec);

    // Take the default processing unless we set this to something else below.
    *pResult = CDRF_DODEFAULT;

    // First thing - check the draw stage. If it's the control's prepaint
    // stage, then tell Windows we want messages for every item.

    //=========================================================================
    if (pCD->nmcd.dwDrawStage == CDDS_PREPAINT)    // before the painting cycle begins
    //=========================================================================
    {
        *pResult = CDRF_NOTIFYITEMDRAW /*| CDRF_NOTIFYPOSTPAINT*/;
    }
    //=========================================================================
    else if (pCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)    // before an item is drawn
    //=========================================================================
    {
        pCD->clrText = pCD->clrTextBk;    // don't want default drawing -
                                        // set text color = background color
        if (hItem)
        {
            CRect rectItem1;
            GetItemRect(hItem, &rectItem1, FALSE);    // get rect for item

            CBrush brush(m_crWindow);
            pDC->FillRect(&rectItem1, &brush);        // erase entire background
        }
        *pResult = CDRF_NOTIFYPOSTPAINT | CDRF_NEWFONT;
    }
    //=========================================================================
    else if (pCD->nmcd.dwDrawStage == CDDS_ITEMPOSTPAINT)    // after an item has been drawn
    //=========================================================================
    {
        // by doing the drawing at this stage we avoid having to draw lines, etc.

        if (m_bFirstTime)
        {
            CImageList *pImageList = GetImageList(TVSIL_NORMAL);
            if (!pImageList)    //+++1.5
            {
                TRACE(_T("WARNING  no image list, setting m_bImages to FALSE\n"));
                m_bImages = FALSE;
            }
        }

        m_bFirstTime = FALSE;

        CRect rectItem;
        GetItemRect(hItem, &rectItem, FALSE);        // get rect for entire item
        CRect rectText;
        GetItemRect(hItem, &rectText, TRUE);        // get rect for text
        rectText.right = rectItem.right;

        XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

        // set up colors

        COLORREF crText = m_crWindowText;
        COLORREF crBackground = m_crWindow;
        COLORREF crTextBackground = m_crWindow;

        BOOL bEnabled = TRUE;

        HTREEITEM hSelected = GetSelectedItem();

        if (pXTCD)
        {

            bEnabled = pXTCD->bEnabled;
            if ((hItem == hSelected) || 
                (GetItemState(hItem, TVIF_STATE) & TVIS_DROPHILITED))
            {
                crTextBackground = m_crHighlight;
            }

            if (!bEnabled)
            {
                crText = /*crAnchorText =*/ m_crGrayText;
            }
            else if ((hItem == hSelected) || 
                (GetItemState(hItem, TVIF_STATE) & TVIS_DROPHILITED))
            {
                crText = /*crAnchorText =*/ m_crHighlightText;
            }
        }

        if (crBackground == -1)
            crBackground = m_crWindow;

        if (pXTCD && pXTCD->bSeparator)            //+++1.6
        {
            if (crText == -1)
                crText = m_crSeparator;

            if (hItem == hSelected)
                crBackground = m_crHighlight;

            DrawSeparator(pDC, hItem, crText, crBackground, rectText);
        }
        else
        {
            if (crText == -1)
                crText = m_crWindowText;

            CString strText = GetItemText(hItem);

            BOOL bContainsHtml = FALSE;

            // check for html tag and char entity
            if (strText.FindOneOf(_T("<&")) >= 0)
                bContainsHtml = TRUE;

                DrawItemText(pDC, hItem, strText, crText, crTextBackground, crBackground, rectText);
        }
    }
}

//=============================================================================
BOOL CXHtmlTree::SelectItem(HTREEITEM hItem)
//=============================================================================
{
    HTREEITEM hPrevItemSel = GetSelectedItem();

    if (hItem == hPrevItemSel)
        return TRUE;

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD && pXTCD->bEnabled)
    {
        NMTREEVIEW nmtv = { 0 };
        
        nmtv.hdr.hwndFrom = m_hWnd;
        nmtv.hdr.idFrom = GetDlgCtrlID();
        nmtv.hdr.code = TVN_SELCHANGED;
        nmtv.itemNew.hItem = hItem;

        CWnd *pWnd = GetParent();
        if (!pWnd)
            pWnd = GetOwner();
        if (pWnd && ::IsWindow(pWnd->m_hWnd))
        {
            pWnd->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmtv);
        }
    }
    else
    {
        if (hPrevItemSel)
            hItem = hPrevItemSel;
        else
            return TRUE;
    }

    return CTreeCtrl::SelectItem(hItem);
}

//=============================================================================
BOOL CXHtmlTree::IsSelected(HTREEITEM hItem)
//=============================================================================
{
    BOOL rc = FALSE;

    if (hItem == GetSelectedItem())
        rc = TRUE;

    return rc;
}

//=============================================================================
BOOL CXHtmlTree::IsEnabled(HTREEITEM hItem)
//=============================================================================
{
    BOOL rc = FALSE;

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)
    {
        rc = pXTCD->bEnabled;
    }

    return rc;
}

//=============================================================================
BOOL CXHtmlTree::IsExpanded(HTREEITEM hItem)
//=============================================================================
{
    BOOL rc = FALSE;

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)
    {
        rc = pXTCD->bExpanded && ItemHasChildren(hItem);
    }

    return rc;
}

//=============================================================================
BOOL CXHtmlTree::IsSeparator(HTREEITEM hItem)            //+++1.6
//=============================================================================
{
    BOOL rc = FALSE;

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)
    {
        rc = pXTCD->bSeparator;
    }

    return rc;
}

//=============================================================================
// GetNormalImageWidth
// returns:  width - if image is specified
//          -width - TV_NOIMAGE is specified for this item
//               0 - no image list
int CXHtmlTree::GetNormalImageWidth(HTREEITEM hItem)
//=============================================================================
{
    int nWidth = 0;

    CImageList *pImageList = GetImageList(TVSIL_NORMAL);

    if (pImageList && hItem)
    {
        // there is an image list
        
        int nImage = TV_NOIMAGE;
        int nSelectedImage = TV_NOIMAGE;
        GetItemImage(hItem, nImage, nSelectedImage);

        IMAGEINFO ii = { 0 };
        if (pImageList->GetImageInfo(0, &ii))    // use first image width
        {
            nWidth = ii.rcImage.right - ii.rcImage.left;
        }

        if (nImage == TV_NOIMAGE)
            nWidth = -nWidth;
    }

    return nWidth;
}

//=============================================================================
BOOL CXHtmlTree::CreateCheckboxImages()
//=============================================================================
{
    CDC *pDC = GetDC();
    ASSERT(pDC);
    BOOL rc = HDCheckboxImageList::CreateCheckboxImageList(pDC, m_StateImage, 
                m_nImageHeight, m_crWindow);
    ReleaseDC(pDC);
    SetImageList(&m_StateImage, TVSIL_STATE);
    return rc;
}

//=============================================================================
int CXHtmlTree::DrawItemText(CDC *pDC, 
                             HTREEITEM hItem, 
                             LPCTSTR lpszText,
                             COLORREF crText, 
                             COLORREF crTextBackground, 
                             COLORREF crBackground, 
                             CRect& rect)
//=============================================================================
{
    ASSERT(pDC);
    ASSERT(hItem);

    if (!pDC || !hItem)
    {
        TRACE(_T("ERROR bad parameters\n"));
        return 0;
    }

    int nWidth = 0;

    CRect rectText(rect);

    pDC->FillSolidRect(&rectText, crBackground);

    CString str = lpszText;

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD && !str.IsEmpty())
    {
        UINT uFormat = DT_VCENTER | DT_SINGLELINE | DT_LEFT | DT_NOPREFIX;

        CFont *pOldFont = NULL;
        CFont font;

        CFont *pFont = pDC->GetCurrentFont();
        if (pFont)
        {
            LOGFONT lf;
            pFont->GetLogFont(&lf);

            font.CreateFontIndirect(&lf);
            pOldFont = pDC->SelectObject(&font);
        }

        pDC->SetTextColor(crText);
        if (crTextBackground == -1)
            pDC->SetBkColor(crBackground);
        else
            pDC->SetBkColor(crTextBackground);

        CRect rectOut(rectText);
        pDC->DrawText(str, &rectOut, uFormat | DT_CALCRECT);
        pDC->DrawText(str, &rectOut, uFormat);
        rectOut.InflateRect(m_nPadding, 0);
        nWidth = rectOut.right;

        if (pOldFont)
            pDC->SelectObject(pOldFont);
    }

    return nWidth;
}


//=============================================================================
int CXHtmlTree::DrawSeparator(CDC *pDC,                        //+++1.6
                              HTREEITEM hItem, 
                              COLORREF crText, 
                              COLORREF crBackground, 
                              CRect& rect)
//=============================================================================
{
    ASSERT(pDC);
    ASSERT(hItem);

    if (!pDC || !hItem)
    {
        TRACE(_T("ERROR bad parameters\n"));
        return 0;
    }

    int nWidth = 0;

    CRect rectSep(rect);
    pDC->FillSolidRect(&rectSep, crBackground);

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)
    {
        CPen pen(PS_SOLID, 1, crText);
        CPen * pOldPen = pDC->SelectObject(&pen);

        TRACE(_T("drawing separator\n"));

        rectSep.right -= 1;
        rectSep.left  -= 2;
        rectSep.top   += rectSep.Height()/2;

        pDC->MoveTo(rectSep.left, rectSep.top);
        pDC->LineTo(rectSep.right, rectSep.top);

        pDC->SelectObject(pOldPen);

        nWidth = rectSep.right;

    }

    return nWidth;
}

//=============================================================================
void CXHtmlTree::OnDestroy()
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::OnDestroy\n"));

    DeleteMap();
    CTreeCtrl::OnDestroy();
}

//=============================================================================
void CXHtmlTree::DeleteMap()
//=============================================================================
{
    BOOL bOldDestroyingTree = m_bDestroyingTree;
    m_bDestroyingTree = TRUE;
    int n = (int)m_DataMap.GetCount();
    POSITION pos = m_DataMap.GetStartPosition();
    HTREEITEM hItem = 0;
    XHTMLTREEDATA *pXTCD = NULL;

    if (n > 0)
    {
        do
        {
            m_DataMap.GetNextAssoc(pos, hItem, pXTCD);

            if (hItem && pXTCD)
                delete pXTCD;

            n--;
        } while (pos != NULL);
    }

    ASSERT(n == 0);

    m_DataMap.RemoveAll();

    m_bDestroyingTree = bOldDestroyingTree;
}

//=============================================================================
// GetNextItem - Get next item in sequence (as if tree was completely expanded)
//   see http://www.codeguru.com/Cpp/controls/treeview/treetraversal/article.php/c645
//   hItem   - The reference item
//   Returns - The item immediately below the reference item
HTREEITEM CXHtmlTree::GetNextItem(HTREEITEM hItem)
//=============================================================================
{
    HTREEITEM hItemNext = NULL;

    ASSERT(hItem);

    if (hItem)
    {
        if (ItemHasChildren(hItem))
        {
            hItemNext = GetChildItem(hItem);    // first child
        }

        if (hItemNext == NULL)
        {
            // return next sibling item - go up the tree to find 
            // a parent's sibling if needed.
            while ((hItemNext = GetNextSiblingItem(hItem)) == NULL)
            {
                if ((hItem = GetParentItem(hItem)) == NULL)
                    return NULL;
            }
        }
    }

    return hItemNext;
}

//=============================================================================
// GetNextItem  - Get previous item as if outline was completely expanded
// Returns      - The item immediately above the reference item
// hItem        - The reference item
HTREEITEM CXHtmlTree::GetPrevItem(HTREEITEM hItem)
//=============================================================================
{
    HTREEITEM hItemPrev;

    hItemPrev = GetPrevSiblingItem(hItem);
    if (hItemPrev == NULL)
        hItemPrev = GetParentItem(hItem);
    else
        hItemPrev = GetLastItem(hItemPrev);
    return hItemPrev;
}

//=============================================================================
// GetLastItem  - Gets last item in the branch
// Returns      - Last item
// hItem        - Node identifying the branch. NULL will 
//                return the last item in outine
HTREEITEM CXHtmlTree::GetLastItem(HTREEITEM hItem)
//=============================================================================
{
    // Last child of the last child of the last child ...
    HTREEITEM hItemNext;
    
    if (hItem == NULL)
    {
        // Get the last item at the top level
        hItemNext = GetRootItem();
        while (hItemNext)
        {
            hItem = hItemNext;
            hItemNext = GetNextSiblingItem(hItemNext);
        }
    }
    
    while (ItemHasChildren(hItem))
    {
        hItemNext = GetChildItem(hItem);
        while (hItemNext)
        {
            hItem = hItemNext;
            hItemNext = GetNextSiblingItem(hItemNext);
        }
    }
    
    return hItem;
}

//=============================================================================
// FindItem  - Finds an item that contains the search string
//
// http://www.codeguru.com/cpp/controls/treeview/treetraversal/article.php/c673/
//
// Returns        - Handle to the item or NULL
//
// str            - String to search for
// bCaseSensitive - Should the search be case sensitive
// bDownDir       - Search direction - TRUE for down
// bWholeWord     - True if search should match whole words
// hItem          - Item to start searching from. NULL for
//                  currently selected item
HTREEITEM CXHtmlTree::FindItem(CString &str, 
                               BOOL bCaseSensitive /*= FALSE*/, 
                               BOOL bDownDir /*= TRUE*/, 
                               BOOL bWholeWord /*= FALSE*/, 
                               BOOL bWrap /* = TRUE */,
                               HTREEITEM hItem /*= NULL*/)
//=============================================================================
{
    int lenSearchStr = str.GetLength();
    if (lenSearchStr == 0) 
        return NULL;

    HTREEITEM hItemSel = hItem ? hItem : GetSelectedItem();
    HTREEITEM hItemCur = bDownDir ? GetNextItem(hItemSel) : GetPrevItem(hItemSel);
    CString sSearch = str;

    if (hItemCur == NULL)
    {
        if (bDownDir)  
            hItemCur = GetRootItem();
        else 
            hItemCur = GetLastItem(NULL);
    }

    if (!bCaseSensitive)
        sSearch.MakeLower();

    while (hItemCur && (hItemCur != hItemSel))
    {
        CString sItemText = GetItemText(hItemCur);

        if (!bCaseSensitive)
            sItemText.MakeLower();

        int n = 0;
        while ((n = sItemText.Find(sSearch)) != -1)
        {
            // search string found
            if (bWholeWord)
            {
                // check preceding char
                if (n != 0)
                {
                    if (isalpha(sItemText[n-1]) || 
                        sItemText[n-1] == '_')
                    {
                        // Not whole word
                        sItemText = sItemText.Right(sItemText.GetLength() - 
                                            n - lenSearchStr);
                        continue;
                    }
                }

                // check succeeding char
                if (sItemText.GetLength() > (n + lenSearchStr) &&
                    (isalpha(sItemText[n+lenSearchStr]) ||
                    sItemText[n+lenSearchStr] == '_' ))
                {
                    // Not whole word
                    sItemText = sItemText.Right(sItemText.GetLength() 
                                    - n - sSearch.GetLength());
                    continue;
                }
            }

            if (IsFindValid( hItemCur))
                return hItemCur;
            else 
                break;
        }

        hItemCur = bDownDir ? GetNextItem(hItemCur) : GetPrevItem(hItemCur);

        if ((hItemCur == NULL) && !bWrap)
            break;

        if ((hItemCur == NULL) && (hItemSel != NULL))    // wrap only if there 
                                                        // is a selected item
        {
            if (bDownDir)  
                hItemCur = GetRootItem();
            else 
                hItemCur = GetLastItem(NULL);
        }
    }
    return NULL;
}

//=============================================================================
// IsFindValid    - Virtual function used by FindItem to allow this
//          function to filter the result of FindItem
// Returns    - True if item matches the criteria
// Arg        - Handle of the item
BOOL CXHtmlTree::IsFindValid(HTREEITEM)
//=============================================================================
{
    return TRUE;
}

//=============================================================================
void CXHtmlTree::RedrawItem(HTREEITEM hItem)
//=============================================================================
{
    if (hItem)
    {
        CRect rect;
        GetItemRect(hItem, &rect, FALSE);
        InvalidateRect(&rect, FALSE);
        UpdateWindow();
    }
}

//=============================================================================
// OnMouseMove - handle link underlining and checkbox hot state
void CXHtmlTree::OnMouseMove(UINT nFlags, CPoint point)
//=============================================================================
{
    // hItem will be non-zero if the cursor is anywhere over a valid item
    UINT flags = 0;
    HTREEITEM hItem = HitTest(point, &flags);

    if (hItem)
    {
        // if mouse is on a different item, or has moved off state icon
        if ((m_hHotItem && (m_hHotItem != hItem)) || 
            (m_hHotItem && ((flags & TVHT_ONITEMSTATEICON) == 0)))
        {
            int nState = GetStateImage(m_hHotItem);

            // a hot item can only be UNCHECKED, CHECKED, or CHECKED_TRISTATE

            nState &= ~HDCheckboxImageList::HOT_INDEX;

            // remove hot from previous hot item
            SetItemState(m_hHotItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);

            m_hHotItem = NULL;
        }

        if ((m_hHotItem == NULL) && (flags & TVHT_ONITEMSTATEICON))
        {
            TRACE(_T("cursor over state image\n"));
            int nState = GetStateImage(hItem);

            if ((nState & HDCheckboxImageList::DISABLED_INDEX) == 0)        // is it enabled?
            {
                // a hot item can only be UNCHECKED, CHECKED, or CHECKED_TRISTATE

                nState |= HDCheckboxImageList::HOT_INDEX;

                // remove hot from previous hot item
                SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);

                m_hHotItem = hItem;
                SetTimer(HOT_TIMER, 100, NULL);        // timer in case mouse leaves window
            }
        }
    }
    
    CTreeCtrl::OnMouseMove(nFlags, point);
}

//=============================================================================
HTREEITEM CXHtmlTree::IsOverItem(LPPOINT lpPoint /*= NULL*/)
//=============================================================================
{
    CPoint point;
    if (lpPoint)
    {
        point = *lpPoint;
    }
    else
    {
        ::GetCursorPos(&point);
        ScreenToClient(&point);
    }
    UINT flags = 0;
    HTREEITEM hItem = HitTest(point, &flags);

    return hItem;
}

//=============================================================================
BOOL CXHtmlTree::IsLeftButtonUp()
//=============================================================================
{
    BOOL rc = FALSE;

    SHORT state = 0;
    if (GetSystemMetrics(SM_SWAPBUTTON))        // check if buttons have been swapped
        state = GetAsyncKeyState(VK_RBUTTON);    // buttons swapped, get right button state
    else
        state = GetAsyncKeyState(VK_LBUTTON);

    // if the most significant bit is set, the button is down
    if (state >= 0)
        rc = TRUE;

    return rc;
}

//=============================================================================
// OnTimer - check if mouse has left, turn off underlining and hot state
void CXHtmlTree::OnTimer(UINT nIDEvent)
//=============================================================================
{
    CPoint point;
    ::GetCursorPos(&point);
    ScreenToClient(&point);

    if (nIDEvent == HOT_TIMER)
    {
        // if mouse has left window, turn off hot and anchor highlighting

        CRect rectClient;
        GetClientRect(&rectClient);

        if (!rectClient.PtInRect(point))
        {
            KillTimer(nIDEvent);

            // mouse has left the window

            if (m_hHotItem)
            {
                int nState = GetStateImage(m_hHotItem);

                // a hot item can only be UNCHECKED, CHECKED, or CHECKED_TRISTATE

                nState &= ~HDCheckboxImageList::HOT_INDEX;

                // remove hot from previous hot item
                SetItemState(m_hHotItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);

                m_hHotItem = NULL;
            }
        }
    }
    else if (nIDEvent == LBUTTONDOWN_TIMER)            // timer set by WM_LBUTTONDOWN
    {
        HTREEITEM hItem = IsOverItem(&point);

        if (IsLeftButtonUp())
        {
            TRACE(_T("mouse button is up >>>>>\n"));

            KillTimer(nIDEvent);

            HTREEITEM hItemSelected = GetSelectedItem();

            if (hItem && (hItem == hItemSelected)
                && !IsSeparator(hItem))    // case 2:  user wants to edit a label
            {
                // clicking on a selected item
                CEdit *pEdit = GetEditControl();

                CRect rect;
                GetItemRect(hItem, &rect, TRUE);
                if (rect.PtInRect(point))
                {
                    TRACE(_T("sending TVM_EDITLABEL\n"));
                    // click on item text, begin edit
                    SendMessage(TVM_EDITLABEL, 0, (LPARAM)hItem);
                }
                else if (pEdit && IsWindow(pEdit->m_hWnd))
                {
                    TRACE(_T("sending WM_CLOSE to edit box\n"));
                    // click outside item text, end edit
                    pEdit->SendMessage(WM_CLOSE);
                }
            }
        }

    }

    CTreeCtrl::OnTimer(nIDEvent);
}

//=============================================================================
BOOL CXHtmlTree::OnEraseBkgnd(CDC* pDC)
//=============================================================================
{
    CRect rectClientx;
    GetClientRect(&rectClientx);
    pDC->FillSolidRect(rectClientx, m_crWindow);
    return TRUE;
}

//=============================================================================
CXHtmlTree& CXHtmlTree::SetCheck(HTREEITEM hItem, BOOL fCheck /*= TRUE*/)
//=============================================================================
{
    ASSERT(hItem);

    if (hItem && m_bCheckBoxes)
    {
        TRACE(_T("in CXHtmlTree::SetCheck: %d  <%s>\n"), fCheck, GetItemText(hItem));
        
        XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

        if (pXTCD && pXTCD->bEnabled && !pXTCD->bSeparator)        //+++1.6
        {
            BOOL bOldChecked = pXTCD->bChecked;
            pXTCD->bChecked = fCheck;

            UINT nState = GetStateImage(hItem);

               SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);

            if (m_bSmartCheck && (bOldChecked != fCheck))
            {
                HTREEITEM hParent = hItem;
                int nCount = 0;
                if (fCheck)
                    nCount = pXTCD->nChildren - pXTCD->nChecked + 1;//bOldCheck ? 1 : 0;
                else
                    nCount = -(pXTCD->nChecked + 1);//bOldCheck ? 1 : 0);

                SetCheckChildren(hItem, fCheck);

                // find all parents, adjust their checked counts
                TRACE(_T("starting nCount=%d\n"), nCount);
                while ((hParent = GetParentItem(hParent)) != NULL)
                {
                    nCount = SetCheckParent(hParent, nCount);
                }
            }

            SendRegisteredMessage(WM_XHTMLTREE_CHECKBOX_CLICKED, hItem, fCheck);
        }
    }

    return *this;
}

//=============================================================================
void CXHtmlTree::SetHotItem(HTREEITEM hItem, UINT nFlags)
//=============================================================================
{
    if (m_hHotItem && (m_hHotItem != hItem))
    {
        int nState = GetStateImage(m_hHotItem);

        // a hot item can only be UNCHECKED, CHECKED, or CHECKED_TRISTATE

        nState &= ~HDCheckboxImageList::HOT_INDEX;

        // remove hot from previous hot item
        SetItemState(m_hHotItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);

        m_hHotItem = NULL;
    }

    if (hItem && (m_hHotItem == NULL) && (nFlags & TVHT_ONITEMSTATEICON))
    {
        TRACE(_T("cursor over state image\n"));
        int nState = GetStateImage(hItem);

        // a hot item can only be UNCHECKED, CHECKED, or CHECKED_TRISTATE

        nState |= HDCheckboxImageList::HOT_INDEX;

        // remove hot from previous hot item
        SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);

        m_hHotItem = hItem;
    }
}

//=============================================================================
LRESULT CXHtmlTree::SendRegisteredMessage(UINT nMessage, 
                                       HTREEITEM hItem, 
                                       LPARAM lParam /*= 0*/)
//=============================================================================
{
    LRESULT lResult = 0;

    CWnd *pWnd = GetParent();
    if (!pWnd)
        pWnd = GetOwner();

    if (pWnd && ::IsWindow(pWnd->m_hWnd))
    {
        XHTMLTREEMSGDATA msgdata = { 0 };
        msgdata.hCtrl    = m_hWnd;
        msgdata.nCtrlId  = GetDlgCtrlID();
        msgdata.hItem    = hItem;

        lResult = pWnd->SendMessage(nMessage, (WPARAM)&msgdata, lParam);
    }

    return lResult;
}

//=============================================================================
int CXHtmlTree::GetStateImage(HTREEITEM hItem)
//=============================================================================
{
    int nState = 0;

    ASSERT(hItem);

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)
    {
        if (m_bSmartCheck && (pXTCD->nChildren != 0))
        {
            if (pXTCD->nChecked == 0)
                nState = UNCHECKED;
            else if (pXTCD->nChecked == (pXTCD->nChildren - pXTCD->nSeparators))    //+++1.6
                nState = CHECKED;
            else
                nState = TRISTATE;
        }
        else
        {
            if (pXTCD->bChecked)
                nState = CHECKED;
            else
                nState = UNCHECKED;
        }
        if (!pXTCD->bEnabled)
            nState |= HDCheckboxImageList::DISABLED_INDEX;
    }

    TRACE(_T("GetStateImage returning %d ~~~~~\n"), nState);

    return nState;
}

//=============================================================================
int CXHtmlTree::SetCheckParent(HTREEITEM hItem, int nCount)
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::SetCheckParent:  nCount=%d  <%s>\n"), nCount, GetItemText(hItem));
    ASSERT(hItem);

    int nState = 0;

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)
    {
        TRACE(_T("pXTCD->nChecked=%d  pXTCD->nChildren=%d \n"), pXTCD->nChecked, pXTCD->nChildren);
        pXTCD->nChecked += nCount;
        if (pXTCD->nChecked < 0)
            pXTCD->nChecked = 0;

        BOOL bOldCheck = pXTCD->bChecked;
        if (pXTCD->nChecked == (pXTCD->nChildren - pXTCD->nSeparators))    //+++1.6
            pXTCD->bChecked = TRUE;
        else
            pXTCD->bChecked = FALSE;

        if (pXTCD->bChecked != bOldCheck)
            nCount += pXTCD->bChecked ? 1 : -1;

        nState = GetStateImage(hItem);

        SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);

        TRACE(_T("nState=%d\n"), nState);
    }

    return nCount;
}

//=============================================================================
CXHtmlTree& CXHtmlTree::SetCheckChildren(HTREEITEM hItem, BOOL fCheck)
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::SetCheckChildren\n"));

    // first set item state for this item
    SetItemStateChildren(hItem, fCheck);

    HTREEITEM hNext = GetChildItem(hItem);
    
    // loop to set item state for children
    while (hNext)
    {
        TRACE(_T("SetCheckChildren: %d  <%s>\n"), fCheck, GetItemText(hNext));

        // recurse into children
        if (ItemHasChildren(hNext))
            SetCheckChildren(hNext, fCheck);

        SetItemStateChildren(hNext, fCheck);

        hNext = GetNextItem(hNext, TVGN_NEXT);
    }

    return *this;
}

//=============================================================================
CXHtmlTree& CXHtmlTree::SetItemStateChildren(HTREEITEM hItem, BOOL fCheck)
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::SetItemStateChildren\n"));

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD && pXTCD->bEnabled)
    {
        int nState = GetStateImage(hItem);
        if (pXTCD->bSeparator)                //+++1.6
        {
            nState = 0;
        }
        else
        {
            int nStateHot = nState & HDCheckboxImageList::HOT_INDEX;            // save hot
            int nStateDisabled = nState & HDCheckboxImageList::DISABLED_INDEX;    // save disabled
            nState &= ~(HDCheckboxImageList::HOT_INDEX | 
                        HDCheckboxImageList::DISABLED_INDEX);    // remove hot & disabled

            pXTCD->bChecked = fCheck;

            if (fCheck)
            {
                pXTCD->nChecked = pXTCD->nChildren - pXTCD->nSeparators;    //+++1.6
                if (pXTCD->nChecked < 0)
                    pXTCD->nChecked = 0;
                nState = CHECKED;
            }
            else
            {
                pXTCD->nChecked = 0;
                nState = UNCHECKED;
            }

            nState |= nStateHot;        // restore hot
            nState |= nStateDisabled;    // restore disabled
            TRACE(_T("setting state to %d\n"), nState);
        }
        SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
    }

    return *this;
}

//=============================================================================
BOOL CXHtmlTree::EnableItem(HTREEITEM hItem, BOOL bEnabled)
//=============================================================================
{
    BOOL rc = TRUE;

    ASSERT(hItem);

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)
    {
        rc = pXTCD->bEnabled;

        pXTCD->bEnabled = bEnabled;

        int nState = GetStateImage(hItem);

        if (bEnabled)
            nState &= ~HDCheckboxImageList::DISABLED_INDEX;
        else
            nState |= HDCheckboxImageList::DISABLED_INDEX;

        // set enabled state
        SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
    }

    return rc;
}

//=============================================================================
BOOL CXHtmlTree::EnableBranch(HTREEITEM hItem, BOOL bEnabled)
//=============================================================================
{
    BOOL rc = TRUE;

    if (hItem && !IsSeparator(hItem))    //+++1.6
    {
        rc = EnableItem(hItem, bEnabled);

        hItem = GetChildItem(hItem);

        if (hItem)
        {
            do
            {
                EnableBranch(hItem, bEnabled);

            } while ((hItem = GetNextSiblingItem(hItem)) != NULL);
        }
    }

    return rc;    // return state of first item
}

//=============================================================================
BOOL CXHtmlTree::GetCheck(HTREEITEM hItem)
//=============================================================================
{
    BOOL rc = FALSE;

    ASSERT(hItem);

    if (m_bCheckBoxes && hItem)
    {
        XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

        if (pXTCD)
        {
            if (m_bSmartCheck)
            {
                if (pXTCD->nChildren == 0)
                {
                    rc = pXTCD->bChecked;
                }
                else
                {
                    if (pXTCD->nChecked == 0)
                        rc = FALSE;        // no children are checked
                    else if (pXTCD->nChecked == (pXTCD->nChildren - pXTCD->nSeparators))    //+++1.6
                        rc = TRUE;        // all children are checked
                    else
                        rc = FALSE;        // not all children are checked
                }
            }
            else
            {
                rc = pXTCD->bChecked;
            }
        }
    }

    return rc;
}

//=============================================================================
HTREEITEM CXHtmlTree::GetFirstCheckedItem()
//=============================================================================
{
    if (m_bCheckBoxes)
    {
        for (HTREEITEM hItem = GetRootItem(); 
             hItem != NULL; 
             hItem = GetNextItem(hItem))
        {
            if (GetCheck(hItem))
                return hItem;
        }
    }

    return NULL;
}

//=============================================================================
HTREEITEM CXHtmlTree::GetNextCheckedItem(HTREEITEM hItem)
//=============================================================================
{
    if (m_bCheckBoxes)
    {
        for (hItem = GetNextItem(hItem); 
             hItem != NULL; 
             hItem = GetNextItem(hItem))
        {
            if (GetCheck(hItem))
                return hItem;
        }
    }

    return NULL;
}

//=============================================================================
HTREEITEM CXHtmlTree::GetPrevCheckedItem(HTREEITEM hItem)
//=============================================================================
{
    if (m_bCheckBoxes)
    {
        for (hItem = GetPrevItem(hItem); 
             hItem != NULL; 
             hItem = GetPrevItem(hItem))
        {
            if (GetCheck(hItem))
                return hItem;
        }
    }

    return NULL;
}

//=============================================================================
BOOL CXHtmlTree::DeleteItem(HTREEITEM hItem)
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::DeleteItem\n"));
    BOOL bOldDestroyingTree = m_bDestroyingTree;

    if (hItem && ItemHasChildren(hItem))
    {
        DeleteBranch(hItem);
    }
    else if (hItem)
    {
        XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

        if (pXTCD)
        {
            HTREEITEM hParent = hItem;

            // find all parents, decrement their children counts,
            // adjust their checked counts and separator counts
            while ((hParent = GetParentItem(hParent)) != NULL)
            {
                IncrementChildren(hParent, -1);
                if (pXTCD->bChecked)
                    SetCheckParent(hParent, -1);
                if (pXTCD->bSeparator)                    //+++1.6
                    IncrementSeparators(hParent, -1);
            }

            m_bDestroyingTree = TRUE;
            m_DataMap.RemoveKey(hItem);
            delete pXTCD;
        }
    }

    m_bDestroyingTree = bOldDestroyingTree;

//    m_nDeleted++;

    return CTreeCtrl::DeleteItem(hItem);
}

//=============================================================================
void CXHtmlTree::DeleteBranch(HTREEITEM hItem)
//=============================================================================
{
    if (hItem)
    {
        HTREEITEM hChild = GetChildItem(hItem);
        while (hChild)
        {
            // recursively delete all the items
            HTREEITEM hNext = GetNextSiblingItem(hChild);
            DeleteBranch(hChild);
            hChild = hNext;
        }
        DeleteItem(hItem);
    }
}

//=============================================================================
CString CXHtmlTree::GetItemNote(HTREEITEM hItem, BOOL bStripHtml /*= FALSE*/)
//=============================================================================
{
    CString strNote = _T("");

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    return strNote;
}

//=============================================================================
HTREEITEM CXHtmlTree::InsertItem(LPTVINSERTSTRUCT lpInsertStruct, 
                                 XHTMLTREEDATA * pData /*= NULL*/)
//=============================================================================
{
    XHTMLTREEDATA *pXTCD = new XHTMLTREEDATA;
    ASSERT(pXTCD);
    if (!pXTCD)
    {
        ASSERT(FALSE);
        return 0;
    }

    if (pData)
    {
        // copy user items for XHTMLTREEDATA
        pXTCD->bChecked   = pData->bChecked;
        pXTCD->bEnabled   = pData->bEnabled;
        pXTCD->bSeparator = pData->bSeparator;    //+++1.6
    
    }

    pXTCD->hTreeCtrl = m_hWnd;

    TVINSERTSTRUCT tvis;
    memcpy(&tvis, lpInsertStruct, sizeof(TVINSERTSTRUCT));

    if (!m_bImages)
    {
        tvis.item.iImage = TV_NOIMAGE;
        tvis.item.iSelectedImage = TV_NOIMAGE;    //+++1.5
    }

    tvis.item.mask |= TVIF_STATE;
    int nState = UNCHECKED;
    if (pXTCD->bChecked && m_bCheckBoxes)
        nState = CHECKED;
    if (!pXTCD->bEnabled)
        nState |= HDCheckboxImageList::DISABLED_INDEX;
    tvis.item.state = INDEXTOSTATEIMAGEMASK(nState);
    tvis.item.stateMask = TVIS_STATEIMAGEMASK;

    CString strText = tvis.item.pszText;

    TRACE(_T("inserting <%s>\n"), strText);

    tvis.item.pszText = strText.LockBuffer();

    HTREEITEM hItem = CTreeCtrl::InsertItem(&tvis);
    ASSERT(hItem);

    strText.UnlockBuffer();

    if (hItem)
    {
        m_DataMap.SetAt(hItem, pXTCD);
        TRACE(_T("count=%d\n"), m_DataMap.GetCount());

        if (m_bSmartCheck)
        {
            HTREEITEM hParent = hItem;

            // find all parents, increment their children counts,
            // adjust their checked counts
            int nCount = pXTCD->bChecked ? 1 : 0;
            while ((hParent = GetParentItem(hParent)) != NULL)
            {
                IncrementChildren(hParent);
                nCount = SetCheckParent(hParent, nCount);
            }
        }
    }

    return hItem;
}

//=============================================================================
HTREEITEM CXHtmlTree::InsertItem(UINT nMask, 
                                 LPCTSTR lpszItem, 
                                 int nImage, 
                                 int nSelectedImage, 
                                 UINT nState, 
                                 UINT nStateMask, 
                                 LPARAM lParam, 
                                 HTREEITEM hParent, 
                                 HTREEITEM hInsertAfter)
//=============================================================================
{
    TVINSERTSTRUCT tvis = { 0 };

    tvis.item.mask = nMask;
    tvis.item.pszText = (LPTSTR)lpszItem;
    tvis.item.iImage = nImage;
    tvis.item.iSelectedImage = nSelectedImage;
    tvis.item.state = nState;
    tvis.item.stateMask = nStateMask;
    tvis.item.lParam = lParam;
    tvis.hParent = hParent;
    tvis.hInsertAfter = hInsertAfter;

    return InsertItem(&tvis);
}

//=============================================================================
HTREEITEM CXHtmlTree::InsertItem(LPCTSTR lpszItem, 
                                 HTREEITEM hParent /*= TVI_ROOT*/, 
                                 HTREEITEM hInsertAfter /*= TVI_LAST*/)
//=============================================================================
{
    TVINSERTSTRUCT tvis = { 0 };

    tvis.item.mask = TVIF_TEXT;
    tvis.item.pszText = (LPTSTR)lpszItem;
    tvis.hParent = hParent;
    tvis.hInsertAfter = hInsertAfter;

    return InsertItem(&tvis);
}

//=============================================================================
HTREEITEM CXHtmlTree::InsertItem(LPCTSTR lpszItem, 
                                 int nImage, 
                                 int nSelectedImage, 
                                 HTREEITEM hParent /*= TVI_ROOT*/, 
                                 HTREEITEM hInsertAfter /*= TVI_LAST*/)
//=============================================================================
{
    TVINSERTSTRUCT tvis = { 0 };

    tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
    tvis.item.pszText = (LPTSTR)lpszItem;
    tvis.item.iImage = nImage;
    tvis.item.iSelectedImage = nSelectedImage;
    tvis.hParent = hParent;
    tvis.hInsertAfter = hInsertAfter;

    return InsertItem(&tvis);
}

//=============================================================================
HTREEITEM CXHtmlTree::InsertSeparator(HTREEITEM hItem)    //+++1.6
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::InsertSeparator\n"));

    HTREEITEM hAfter = hItem;
    HTREEITEM hParent = GetParentItem(hItem);
    HTREEITEM hNewParent = GetParentItem(hItem);
    TRACE(_T("hParent=%X\n"), hParent);

    if (hParent == hAfter)
    {
        // dropping on parent
        hNewParent = hParent;
        hAfter = TVI_FIRST;
    }
    else if (ItemHasChildren(hAfter) && IsExpanded(hAfter))
    {
        // dropping on node that is expanded
        hNewParent = hAfter;
        hAfter = TVI_FIRST;
    }

    TVINSERTSTRUCT tvis = { 0 };
    tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
    tvis.item.pszText = _T("");
    tvis.item.iImage = TV_NOIMAGE;
    tvis.item.iSelectedImage = TV_NOIMAGE;
    tvis.hParent = hNewParent;
    tvis.hInsertAfter = hAfter;

    HTREEITEM hSep = InsertItem(&tvis);

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hSep);

    if (pXTCD)
    {
        pXTCD->bSeparator = TRUE;
    }

    SetItemState(hSep, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK);

    // increment separator count in parents
    hParent = hSep;
    while ((hParent = GetParentItem(hParent)) != NULL)
    {
        IncrementSeparators(hParent, 1);
    }

    return hSep;
}

//=============================================================================
int CXHtmlTree::IncrementChildren(HTREEITEM hItem, int n /*= 1*/)
//=============================================================================
{
    int nChildren = 0;

    ASSERT(hItem);

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)
    {
        pXTCD->nChildren += n;
        if (pXTCD->nChildren < 0)
            pXTCD->nChildren = 0;
        nChildren = pXTCD->nChildren;
    }

    return nChildren;
}

//=============================================================================
int CXHtmlTree::IncrementSeparators(HTREEITEM hItem, int n /*= 1*/)        //+++1.6
//=============================================================================
{
    int nSeparators = 0;

    ASSERT(hItem);

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)
    {
        pXTCD->nSeparators += n;
        if (pXTCD->nSeparators < 0)
            pXTCD->nSeparators = 0;
        nSeparators = pXTCD->nSeparators;
    }

    return nSeparators;
}

//=============================================================================
XHTMLTREEDATA * CXHtmlTree::GetItemDataStruct(HTREEITEM hItem)
//=============================================================================
{
    XHTMLTREEDATA *pXTCD = NULL;

    if (hItem && !m_bDestroyingTree)
    {
        m_DataMap.Lookup(hItem, pXTCD);
    }

    return pXTCD;
}

//=============================================================================
void CXHtmlTree::OnSysColorChange() 
//=============================================================================
{
    CTreeCtrl::OnSysColorChange();
    SetColors();    
}

//=============================================================================
COLORREF CXHtmlTree::GetItemTextBkColor(HTREEITEM hItem)
//=============================================================================
{
    COLORREF rc = 0;

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    return rc;
}

//=============================================================================
COLORREF CXHtmlTree::GetItemTextColor(HTREEITEM hItem)
//=============================================================================
{
    COLORREF rc = 0;

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
    return rc;
}

//=============================================================================
BOOL CXHtmlTree::SetItemText(HTREEITEM hItem, LPCTSTR lpszItem)        //+++1.6
//=============================================================================
{
    if (IsSeparator(hItem))
        return FALSE;

    return CTreeCtrl::SetItemText(hItem, lpszItem);
}

//=============================================================================
COLORREF CXHtmlTree::SetItemTextBkColor(HTREEITEM hItem, COLORREF rgb)
//=============================================================================
{
    COLORREF rc = 0;

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    return rc;
}

//=============================================================================
COLORREF CXHtmlTree::SetItemTextColor(HTREEITEM hItem, COLORREF rgb)
//=============================================================================
{
    COLORREF rc = 0;

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    return rc;
}

//=============================================================================
COLORREF CXHtmlTree::SetBkColor(COLORREF rgb) 
//=============================================================================
{
    COLORREF old = m_crWindow; 
    if (rgb == -1)
        rgb = GetSysColor(COLOR_WINDOW);
    m_crCustomWindow = m_crWindow = rgb; 
    return old; 
}

//=============================================================================
COLORREF CXHtmlTree::SetTextColor(COLORREF rgb) 
//=============================================================================
{
    COLORREF old = m_crWindowText; 
    if (rgb == -1)
        rgb = GetSysColor(COLOR_WINDOWTEXT);
    m_crCustomWindowText = m_crWindowText = rgb; 
    return old; 
}

//=============================================================================
COLORREF CXHtmlTree::SetInsertMarkColor(COLORREF rgb)
//=============================================================================
{
    if (rgb == -1)
        rgb = GetSysColor(COLOR_HIGHLIGHT);
    m_crInsertMark = rgb; 
    return CTreeCtrl::SetInsertMarkColor(rgb);
}

//=============================================================================
CXHtmlTree& CXHtmlTree::SetSeparatorColor(COLORREF rgb)    //+++1.6
//=============================================================================
{
    if (rgb == -1)
        rgb = GetSysColor(COLOR_GRAYTEXT);
    m_crSeparator = rgb; 
    return *this;
}

//=============================================================================
void CXHtmlTree::SetColors()
//=============================================================================
{
    m_crWindow        = GetSysColor(COLOR_WINDOW);
    m_crWindowText    = GetSysColor(COLOR_WINDOWTEXT);
    m_crGrayText      = GetSysColor(COLOR_GRAYTEXT);
    m_crHighlight     = GetSysColor(COLOR_HIGHLIGHT);
    m_crHighlightText = GetSysColor(COLOR_HIGHLIGHTTEXT);
    m_crInsertMark    = GetSysColor(COLOR_HIGHLIGHT);
    m_crSeparator     = GetSysColor(COLOR_GRAYTEXT);        //+++1.6

    if (m_crCustomWindow != -1)
        m_crWindow = m_crCustomWindow;

    if (m_crCustomWindowText != -1)
        m_crWindowText = m_crCustomWindowText;
}

//=============================================================================
BOOL CXHtmlTree::EnableWindow(BOOL bEnable /*= TRUE*/)
//=============================================================================
{
    BOOL rc = CTreeCtrl::EnableWindow(bEnable);

    if (bEnable)
    {
        if (m_crCustomWindow != -1)
            m_crWindow = m_crCustomWindow;
        else
            m_crWindow = GetSysColor(COLOR_WINDOW);

        if (m_crCustomWindowText != -1)
            m_crWindowText = m_crCustomWindowText;
        else
            m_crWindowText = GetSysColor(COLOR_WINDOWTEXT);
    }
    else
    {
        m_crWindow = GetDisabledColor(GetSysColor(COLOR_WINDOW));
        m_crWindowText = GetSysColor(COLOR_GRAYTEXT);
    }

    return rc;
}

//=============================================================================
COLORREF CXHtmlTree::GetDisabledColor(COLORREF color)
//=============================================================================
{
    BYTE r = GetRValue(color);
    BYTE g = GetGValue(color);
    BYTE b = GetBValue(color);
    const BYTE disabled_value = 10;

    r = (r >= disabled_value) ? (BYTE)(r - disabled_value) : r;
    g = (g >= disabled_value) ? (BYTE)(g - disabled_value) : g;
    b = (b >= disabled_value) ? (BYTE)(b - disabled_value) : b;

    return RGB(r, g, b);
}

//=============================================================================
BOOL CXHtmlTree::DeleteAllItems()
//=============================================================================
{
    // avoid unneeded selchange notifications
    SelectItem(NULL);

    CollapseAll();
    BOOL bOldDestroyingTree = m_bDestroyingTree;
    m_bDestroyingTree = TRUE;
    BOOL rc = CTreeCtrl::DeleteAllItems();
    DeleteMap();
    m_bDestroyingTree = bOldDestroyingTree;
    return rc;
}

//=============================================================================
void CXHtmlTree::CollapseAll()
//=============================================================================
{
    HTREEITEM hItemRoot = GetRootItem();

    HTREEITEM hItem = hItemRoot;

    if (hItem)
    {
        do 
        {
            CollapseBranch(hItem);

        } while ((hItem = GetNextSiblingItem(hItem)) != NULL);

        SelectItem(hItemRoot);

        SendMessage(WM_HSCROLL, SB_LEFT);
        UpdateWindow();
    }
}

//=============================================================================
void CXHtmlTree::ExpandAll()
//=============================================================================
{
    HTREEITEM hItemSel = GetSelectedItem();
    if (!hItemSel)
        hItemSel = GetRootItem();

    if (hItemSel)
    {
        HTREEITEM hItem = GetRootItem();    // must start with root for best performance

        SetRedraw(FALSE);

        do 
        {
            ExpandBranch(hItem);

        } while ((hItem = GetNextSiblingItem(hItem)) != NULL);

        SelectItem(hItemSel);
        SetScrollPos(SB_VERT, 0);
        EnsureVisible(hItemSel);
        SendMessage(WM_HSCROLL, SB_LEFT);

        SetRedraw(TRUE);
    }
}

//=============================================================================
void CXHtmlTree::ExpandBranch(HTREEITEM hItem)
//=============================================================================
{
    if (hItem && ItemHasChildren(hItem))
    {
        Expand(hItem, TVE_EXPAND);

        hItem = GetChildItem(hItem);

        if (hItem)
        {
            do
            {
                ExpandBranch(hItem);

            } while ((hItem = GetNextSiblingItem(hItem)) != NULL);
        }
    }
}

//=============================================================================
void CXHtmlTree::CollapseBranch(HTREEITEM hItem)
//=============================================================================
{
    if (hItem && ItemHasChildren(hItem))
    {
        Expand(hItem, TVE_COLLAPSE);

        hItem = GetChildItem(hItem);

        if (hItem && ItemHasChildren(hItem))
        {
            do 
            {
                CollapseBranch(hItem);

            } while ((hItem = GetNextSiblingItem(hItem)) != NULL);
        }
    }
}

//=============================================================================
BOOL CXHtmlTree::Expand(HTREEITEM hItem, UINT nCode)
//=============================================================================
{
    if (hItem && ItemHasChildren(hItem))
    {
        XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

        if (pXTCD && pXTCD->bEnabled)
        {
            BOOL bOldExpanded = pXTCD->bExpanded;

            if (nCode == TVE_COLLAPSE || nCode == TVE_COLLAPSERESET)
            {
                pXTCD->bExpanded = FALSE;
            }
            else if (nCode == TVE_EXPAND)
            {
                pXTCD->bExpanded = TRUE;
            }
            else if (nCode == TVE_TOGGLE)
            {
                if (bOldExpanded)
                    pXTCD->bExpanded = FALSE;
                else
                    pXTCD->bExpanded = TRUE;
            }
            else
            {
                TRACE(_T("ERROR bad nCode=%u\n"), nCode);
            }

            if (pXTCD->bExpanded)
                pXTCD->bHasBeenExpanded = TRUE;

            if (pXTCD->bExpanded != bOldExpanded)
                SendRegisteredMessage(WM_XHTMLTREE_ITEM_EXPANDED, hItem, 
                    pXTCD->bExpanded);
        }
    }

    TRACE(_T("calling CTreeCtrl::Expand()\n"));
    BOOL rc = CTreeCtrl::Expand(hItem, nCode);

    return rc;
}

//=============================================================================
BOOL CXHtmlTree::GetHasBeenExpanded(HTREEITEM hItem)
//=============================================================================
{
    BOOL rc = FALSE;

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)
    {
        rc = pXTCD->bHasBeenExpanded;
    }

    return rc;
}

//=============================================================================
void CXHtmlTree::CheckAll(BOOL bCheck)
//=============================================================================
{
    if (m_bCheckBoxes)
    {
        if (m_bSmartCheck)
        {
            // check all root-level items
            HTREEITEM hRoot = GetRootItem();

            while (hRoot)
            {
                SetCheck(hRoot, bCheck);
                hRoot = GetNextItem(hRoot, TVGN_NEXT);    // get next root item
            }
        }
        else
        {
            // check all items
            HTREEITEM hItem = GetRootItem();

            while (hItem)
            {
                SetCheck(hItem, bCheck);
                hItem = GetNextItem(hItem);        // get next sequential item
            }
        }
    }
}

//=============================================================================
int CXHtmlTree::GetCheckedCount()
//=============================================================================
{
    int rc = 0;

    if (!m_bCheckBoxes)
        return 0;

    for (HTREEITEM hItem = GetRootItem();
         hItem != NULL; 
         hItem = GetNextItem(hItem))
    {
        if (GetCheck(hItem))
            rc++;
    }

    return rc;
}

//=============================================================================
int CXHtmlTree::GetChildrenCheckedCount(HTREEITEM hItem)
//=============================================================================
{
    int rc = 0;

    if (!m_bCheckBoxes)
        return 0;

    if (!hItem)
        hItem = GetRootItem();

    if (hItem && m_bSmartCheck)
    {
        XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

        if (pXTCD)
        {
            rc = pXTCD->nChecked;
        }
    }
    else if (hItem && ItemHasChildren(hItem))
    {
        HTREEITEM hChild = GetChildItem(hItem);
        if (hChild)
        {
            do
            {
                if (GetCheck(hChild))
                    rc++;

                rc += GetChildrenCheckedCount(hChild);

            } while ((hChild = GetNextSiblingItem(hChild)) != NULL);
        }
    }

    return rc;
}

//=============================================================================
int CXHtmlTree::GetChildrenDisabledCount(HTREEITEM hItem)
//=============================================================================
{
    int rc = 0;

    if (!hItem)
        hItem = GetRootItem();

    if (hItem && ItemHasChildren(hItem))
    {
        HTREEITEM hChild = GetChildItem(hItem);
        if (hChild)
        {
            do
            {
                if (!IsEnabled(hChild))
                    rc++;

                rc += GetChildrenDisabledCount(hChild);

            } while ((hChild = GetNextSiblingItem(hChild)) != NULL);
        }
    }

    return rc;
}

//=============================================================================
int CXHtmlTree::GetChildrenCount(HTREEITEM hItem)
//=============================================================================
{
    int rc = 0;

    if (!hItem)
        hItem = GetRootItem();

    if (hItem && m_bSmartCheck)
    {
        XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

        if (pXTCD)
        {
            rc = pXTCD->nChildren;
        }
    }
    else if (hItem && ItemHasChildren(hItem))
    {
        HTREEITEM hChild = GetChildItem(hItem);
        if (hChild)
        {
            do
            {
                rc++;
                rc += GetChildrenCount(hChild);

            } while ((hChild = GetNextSiblingItem(hChild)) != NULL);
        }
    }

    return rc;
}

//=============================================================================
int CXHtmlTree::GetSeparatorCount(HTREEITEM hItem)            //+++1.6
//=============================================================================
{
    int rc = 0;

    if (!hItem)
        hItem = GetRootItem();

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)
    {
        rc = pXTCD->nSeparators;
    }

    return rc;
}

//=============================================================================
CString CXHtmlTree::GetItemText(HTREEITEM hItem, BOOL bStripHtml /*= FALSE*/) const
//=============================================================================
{
    CString strText = _T("");

    if (hItem)
    {
        strText = CTreeCtrl::GetItemText(hItem);

        if (bStripHtml)
        {
        }
    }

    return strText;
}

//=============================================================================
void CXHtmlTree::OnLButtonDown(UINT nFlags, CPoint point)
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::OnLButtonDown\n"));

    UINT uFlags = 0;
    HTREEITEM hItem = HitTest(point, &uFlags);
    HTREEITEM hItemSelected = GetSelectedItem();
    CEdit *pEdit = GetEditControl();

    if (hItem && (TVHT_ONITEMBUTTON & uFlags))
    {
        TRACE(_T("TVHT_ONITEMBUTTON\n"));
        if (pEdit && IsWindow(pEdit->m_hWnd))
        {
            TRACE(_T("sending WM_CLOSE to edit box\n"));
            pEdit->SendMessage(WM_CLOSE);
        }

        XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

        if (pXTCD && !pXTCD->bEnabled)
            return;

        if (pXTCD && pXTCD->bEnabled && ItemHasChildren(hItem))
        {
            BOOL bExpanded = !pXTCD->bExpanded;
            Expand(hItem, bExpanded ? TVE_EXPAND : TVE_COLLAPSE);
            return;
        }
    }
    else if (hItem && (TVHT_ONITEMSTATEICON & uFlags))
    {
        TRACE(_T("TVHT_ONITEMSTATEICON\n"));
        // click on checkbox
    }
    else if (hItem && IsSeparator(hItem))        //+++1.6
    {
        if (hItem != hItemSelected)
            SelectItem(hItem);

        m_hItemButtonDown = hItem;
        TRACE(_T("setting LBUTTONDOWN_TIMER >>>>>\n"));
        SetTimer(LBUTTONDOWN_TIMER, 100, NULL);
        return;
    }
    else if (hItem && (hItem == hItemSelected))
    {
        // item was already selected, so catch the button up state -
        // this will be start of edit. We don't need to check if disabled, 
        // because disabled items can't be selected
        //
        // NOTE: we use timer because we don't always get WM_LBUTTONUP message.
        //
        m_hItemButtonDown = hItem;
        TRACE(_T("setting LBUTTONDOWN_TIMER >>>>>\n"));
        SetTimer(LBUTTONDOWN_TIMER, 100, NULL);

        // We disable the return to prevent edit box opening on button down -
        // it should open only on button up.
        // For drag & drop, we need to call base function to allow 
        // OnBegindrag() to be called.
        return;

    }

    TRACE(_T("calling CTreeCtrl::OnLButtonDown\n"));

    CTreeCtrl::OnLButtonDown(nFlags, point);
}

//=============================================================================
// OnClick - handle clicking on checkbox and link
BOOL CXHtmlTree::OnClick(NMHDR* /*pNMHDR*/, LRESULT* pResult)
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::OnClick\n"));

    BOOL rc = FALSE;    // allow parent to handle

    CPoint point;
    ::GetCursorPos(&point);
    ScreenToClient(&point);

    UINT flags = 0;
    HTREEITEM hItem = HitTest(point, &flags);
    TRACE(_T("in CXHtmlTree::OnClick:  hItem=%X\n"), hItem);

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD)        // will not be NULL if hItem is valid
    {
        if (pXTCD->bSeparator)            //+++1.6
        {
            return TRUE;    // can't set checkbox if separator
        }
        else if (flags & TVHT_ONITEMSTATEICON)
        {
            TRACE(_T("click on checkbox\n"));

            if (pXTCD->bEnabled /*&& !m_bReadOnly*/)
            {
                SetCheck(hItem, !pXTCD->bChecked);

                if (m_bSelectFollowsCheck)
                    SelectItem(hItem);

                int nState = GetStateImage(hItem);
                nState |= HDCheckboxImageList::HOT_INDEX;

                SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);

                m_hHotItem = hItem;
            }
            else
            {
                rc = TRUE;
            }
        }
        else
        {
            TRACE(_T("not on checkbox\n"));

                if (!pXTCD->bEnabled)
            {
                TRACE(_T("click on disabled item\n"));
                rc = TRUE;
            }
        }
    }

    *pResult = 0;
    return rc;
}

//=============================================================================
BOOL CXHtmlTree::OnDblclk(NMHDR* /*pNMHDR*/, LRESULT* pResult) 
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::OnDblclk\n"));
    BOOL rc = FALSE;    // allow parent to handle

    CPoint point;
    ::GetCursorPos(&point);
    ScreenToClient(&point);

    UINT flags = 0;
    HTREEITEM hItem = HitTest(point, &flags);
    TRACE(_T("in CXHtmlTree::OnDblclk:  hItem=%X\n"), hItem);

    if (hItem && (flags & (TVHT_ONITEM | TVHT_ONITEMBUTTON | TVHT_ONITEMSTATEICON)))
    {
        XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

        if (pXTCD && !pXTCD->bEnabled)
        {
            TRACE(_T("double click on disabled item\n"));
            rc = TRUE;        // don't allow default processing
        }
        else if (pXTCD && pXTCD->bEnabled && ItemHasChildren(hItem))
        {
            pXTCD->bExpanded = !pXTCD->bExpanded;
            pXTCD->bHasBeenExpanded = TRUE;
            SendRegisteredMessage(WM_XHTMLTREE_ITEM_EXPANDED, hItem, 
                pXTCD->bExpanded);
        }
    }

    *pResult = rc;
    return rc;
}

//=============================================================================
BOOL CXHtmlTree::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) 
//=============================================================================
{
    BOOL rc = FALSE;    // allow parent to handle

    NMTREEVIEW* pNMTreeView = (NMTREEVIEW*)pNMHDR;
    
    HTREEITEM hItem = pNMTreeView->itemNew.hItem;
    HTREEITEM hOldItem = pNMTreeView->itemOld.hItem;

    CString strItem = GetItemText(hItem);
    TRACE(_T("in CXHtmlTree::OnSelchanged:  <%s>\n"), strItem);

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD && !pXTCD->bEnabled)
    {
        if (hOldItem)
        {
            SelectItem(hOldItem);
        }
        rc = TRUE;
    }
    else
    {
        if (hItem == m_hPreviousItem)
            rc = TRUE;
    }
    
    if (!rc)
        m_hPreviousItem = hItem;

    *pResult = rc;
    return rc;
}

//=============================================================================
BOOL CXHtmlTree::OnSelchanging(NMHDR* pNMHDR, LRESULT* pResult) 
//=============================================================================
{
    BOOL rc = FALSE;    // allow parent to handle

    NMTREEVIEW* pNMTreeView = (NMTREEVIEW*)pNMHDR;
    
    HTREEITEM hItem = pNMTreeView->itemNew.hItem;

    CString strItem = GetItemText(hItem);
    TRACE(_T("in CXHtmlTree::OnSelchanging:  <%s>\n"), strItem);

    XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);

    if (pXTCD && !pXTCD->bEnabled)
    {
        rc = TRUE;
    }

    *pResult = rc;
    return rc;
}

//=============================================================================
void CXHtmlTree::OnSize(UINT nType, int cx, int cy) 
//=============================================================================
{
    TRACE(_T("in CXHtmlTree::OnSize\n"));

    CTreeCtrl::OnSize(nType, cx, cy);

    m_bFirstTime = TRUE;    // this will cause tooltips to be re-created
}

//=============================================================================
int CXHtmlTree::GetIndentLevel(HTREEITEM hItem)
//=============================================================================
{
    int nIndent = 0;

    while ((hItem = GetParentItem(hItem)) != NULL)
        nIndent++;

    return nIndent;
}
